package com.hgkj.system.service.impl;

import com.hgkj.common.core.constant.DictConstant;
import com.hgkj.common.core.utils.AssertExceptionUtils;
import com.hgkj.system.domain.SysRole;
import com.hgkj.system.domain.dto.request.AssignRoleMenu;
import com.hgkj.system.domain.mid.RoleMenuMid;
import com.hgkj.system.domain.vo.RoleMenuTree;
import com.hgkj.system.mapper.SysRoleMapper;
import com.hgkj.system.mapper.SysRoleMenuMapper;
import com.hgkj.system.service.SysRoleService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@Slf4j
public class SysRoleServiceImpl implements SysRoleService {

    private final SysRoleMapper sysRoleMapper;
    private final SysRoleMenuMapper sysRoleMenuMapper;

    public SysRoleServiceImpl(SysRoleMapper sysRoleMapper, SysRoleMenuMapper sysRoleMenuMapper) {
        this.sysRoleMapper = sysRoleMapper;
        this.sysRoleMenuMapper = sysRoleMenuMapper;
    }

    /**
     * 根据用户ID获取用户角色集合
     *
     * @param userId 用户ID
     * @return 拥有的角色集合
     */
    @Override
    public List<String> getRolesByUserId(long userId) {
        return sysRoleMapper.selectRolesByUserId(userId);
    }

    /**
     * 查询角色列表
     *
     * @param role 附加信息
     * @return list
     */
    @Override
    public List<SysRole> getRoleList(SysRole role) {
        return sysRoleMapper.selectRoles(role);
    }

    /**
     * 插入角色信息
     *
     * @param role 角色信息
     * @return 操作行数
     */
    @Override
    public int insertRole(SysRole role) {
        SysRole dbRoleByName = sysRoleMapper.selectRoleByRoleName(role.getRoleName());
        AssertExceptionUtils.mustNull(dbRoleByName, "角色名重复");

        SysRole dbRoleByKey = sysRoleMapper.selectRoleByRoleKey(role.getRoleKey());
        AssertExceptionUtils.mustNull(dbRoleByKey, "角色权限标识重复");

        role.initInsertPropriety();
        return sysRoleMapper.insertRole(role);
    }

    /**
     * 修改角色信息
     *
     * @param role 角色信息
     * @return 操作行数
     */
    @Override
    public int updateRole(SysRole role) {
        SysRole dbRoleByName = sysRoleMapper.selectRoleByRoleName(role.getRoleName());
        if (!Objects.isNull(dbRoleByName)) {
            AssertExceptionUtils.mustEquals(dbRoleByName.getRoleId(), role.getRoleId(), "角色名重复");
        }
        SysRole dbRoleByKey = sysRoleMapper.selectRoleByRoleKey(role.getRoleKey());
        if (!Objects.isNull(dbRoleByKey)) {
            AssertExceptionUtils.mustEquals(dbRoleByKey.getRoleId(), role.getRoleId(), "角色权限标识重复");
        }
        role.initUpdatePropriety();
        return sysRoleMapper.updateRole(role);
    }

    /**
     * 删除角色
     *
     * @param roleId 角色ID
     * @return 操作行数
     */
    @Override
    public int deleteRole(Long roleId) {
        return sysRoleMapper.deleteRoleById(roleId);
    }

    /**
     * 查询角色的权限树
     *
     * @param roleId 角色ID
     * @return permsTree
     */
    @Override
    public Map<String, Object> assignList(Long roleId) {
        Map<String, Object> ans = new HashMap<>();
        List<RoleMenuMid> allMenu = sysRoleMenuMapper.selectAllMenu();
        List<Long> checkedMenuIds = sysRoleMenuMapper.selectMenuIdsByRoleId(roleId);
        List<RoleMenuTree> menuTrees = buildRoleMenuTree(allMenu);
        Set<Long> expandedMenuIds = getExpandMenuIds(menuTrees, checkedMenuIds);
        ans.put("checkedMenuIds", checkedMenuIds);
        ans.put("menuTrees", menuTrees);
        ans.put("expandedMenuIds", expandedMenuIds);
        return ans;
    }

    /**
     * 获取含有被选中的子节点的menuId集合
     *
     * @param menuTrees      多路树
     * @param checkedMenuIds 选中的节点
     * @return menuIds
     */
    private Set<Long> getExpandMenuIds(List<RoleMenuTree> menuTrees, List<Long> checkedMenuIds) {
        Set<Long> expandMenuIdList = new HashSet<>();
        if (checkedMenuIds == null || checkedMenuIds.isEmpty()) {
            return expandMenuIdList;
        }
        // 广度优先遍历
        Queue<RoleMenuTree> queue = new LinkedList<>();
        // 初始化队列
        for (RoleMenuTree root : menuTrees) {
            queue.offer(root);
        }
        // 迭代队列所有节点
        while (!queue.isEmpty()) {
            RoleMenuTree cur = queue.poll();

            // cur被选中就将父节点加入
            if (checkedMenuIds.contains(cur.getMenuId()) && !Objects.equals(cur.getParentId(), DictConstant.parentId)) {
                expandMenuIdList.add(cur.getParentId());
            }
            List<RoleMenuTree> curChildren = cur.getChildren();
            if (!Objects.isNull(curChildren) && !curChildren.isEmpty()) {
                // 自身被选中且有子节点默认展开
                if (checkedMenuIds.contains(cur.getMenuId())) {
                    expandMenuIdList.add(cur.getMenuId());
                }
                // 将子节点加入队列
                for (RoleMenuTree child : curChildren) {
                    queue.offer(child);
                }
            }
        }
        return expandMenuIdList;
    }

    /**
     * 分配角色菜单权限
     *
     * @param assignRoleMenu assign
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assign(AssignRoleMenu assignRoleMenu) {
        // 删除原有权限
        int removeRows = sysRoleMenuMapper.deleteByRoleId(assignRoleMenu.getRoleId());
        log.info("[删除分配菜单数据条数:{},角色ID:{}]", removeRows, assignRoleMenu.getRoleId());
        // 设置新的权限
        if (assignRoleMenu.getMenuIds().length == 0) {
            return;
        }
        int insertRows = sysRoleMenuMapper.batchInsert(assignRoleMenu.getRoleId(), assignRoleMenu.getMenuIds());
        log.info("[新分配权限条数:{},角色ID:{}]", insertRows, assignRoleMenu.getRoleId());
    }

    private List<RoleMenuTree> buildRoleMenuTree(List<RoleMenuMid> roleMenuMidList) {
        if (roleMenuMidList == null || roleMenuMidList.isEmpty()) {
            return Collections.emptyList();
        }
        // 构建menuId与自身的映射同时转化object
        Map<Long, RoleMenuTree> menuIdAndSelf = new HashMap<>();
        for (RoleMenuMid roleMenuMid : roleMenuMidList) {
            RoleMenuTree roleMenuTree = new RoleMenuTree();
            roleMenuTree.setMenuId(roleMenuMid.getMenuId());
            roleMenuTree.setParentId(roleMenuMid.getParentId());
            roleMenuTree.setLabel(roleMenuMid.getMenuName());
            roleMenuTree.setChildren(new ArrayList<>());
            menuIdAndSelf.put(roleMenuTree.getMenuId(), roleMenuTree);
        }
        List<RoleMenuTree> ans = new ArrayList<>();
        for (Map.Entry<Long, RoleMenuTree> menuTreeEntry : menuIdAndSelf.entrySet()) {
            if (menuTreeEntry.getValue().getParentId().equals(DictConstant.parentId)) {
                ans.add(menuTreeEntry.getValue());
            } else {
                if (menuIdAndSelf.containsKey(menuTreeEntry.getValue().getParentId())) {
                    menuIdAndSelf.get(menuTreeEntry.getValue().getParentId()).getChildren().add(menuTreeEntry.getValue());
                }
            }
        }
        return ans;
    }

}
